<?php
namespace Infinity;

abstract class AbstractEntity {
    protected $entity_type;
    protected $entity;
    protected $entity_info;
    protected $bundle;
    
    abstract protected function init();

    public function __construct($id=null) {
        $this->init();
        if($id) {
            $entity = false;
            if(is_numeric($id))
                $entity = entity_load_single($this->entity_type, $id);
            if(is_array($id)) {
                $entity = entity_create($this->entity_type, $id);
                if($this->bundle())
                    $entity->type = $this->bundle();
            }
            if(is_object($id))
                $entity = $id;
            if($entity) {
                $this->setEntity($entity);
            }
        }
    }

    function __get($property_name)
    {
        return $this->get($property_name);
    }
  
    function __set($property_name, $value)
    {
        $this->set($property_name, $value);
    }

    public function set($field, $data) {
        if(strpos($field, 'field_') === 0 || $field == 'body') {
            return $this->setField($field, $data);
        }
        $this->entity->{$field} = $data;
        return $this;
    }

    public function get($field, $langcode = NULL) {
        if(empty($this->entity)) {
            $this->printBacktrace();
            throw new \Exception('Unset entity data');
        }
        if(strpos($field, 'field_') === 0) {
            // field
            $items = $this->getFieldItems($field, $langcode);
            try {
                $fieldRow = new FieldRow($items, $this->entity, $this->entity_type, $field);
            } catch(\Exception $e) {
                $this->printBacktrace();
                throw $e;
            }
            return $fieldRow;
        }
        return $this->entity->{$field};
    }

    public function id() {
        return entity_id($this->entity_type, $this->entity);
    }

    public function getFieldItems($field, $langcode = NULL) {
        if(empty($this->entity)) {
            $this->printBacktrace();
            throw new \Exception('Unset entity data, field:'.$field);
        }
        return field_get_items($this->entity_type, $this->entity, $field, $langcode);
    }

    // 将废弃
    public function getFieldValueRender($field, $index = 0) {
        $items = field_get_items($this->entity_type, $this->entity, $field);
        $value = field_view_value($this->entity_type, $this->entity, $field, $items[$index]);
        return render($value);
    }

    public function hasColumn($field, $column = 'value', $index = 0) {
        if($this->hasField($field, $index)) {
            $items = $this->getFieldItems($field);
            $item = $items[$index];
            return isset($item[$column]);
        }
        return false;
    }

    public function hasField($field, $index = 0) {
        if(empty($this->entity)) {
            $this->printBacktrace();
            throw new \Exception('Unset entity data, field:'.$field);
        }
        $items = $this->getFieldItems($field);
        if(empty($items[$index]) || !is_array($items[$index])) return false;
        return true;
    }

    public function is($field, $value, $column='value') {
        if(empty($this->entity)) {
            $this->printBacktrace();
            throw new \Exception('Unset entity data, field:'.$field);
        }
        if(is_array($value))
            $values = $value;
        else
            $values = array($value);
        if($this->hasColumn($field, $column)) {
            foreach($values as $v) {
                if($this->get($field)->{$column} == $v) {
                    return true;
                }
            }
        }
        return false;
    }

    public function setField($field, $data, $index = 0,  $langcode=LANGUAGE_NONE) {
        if($data instanceof \stdClass) {
            $data = (array)$data;
        }
        if(is_array($data) && count($data) == 0) {
            $this->entity->{$field} = array();
        } elseif(is_array($data))
            $this->entity->{$field}[$langcode][$index] = $data;
        else
            $this->entity->{$field}[$langcode][$index]['value'] = $data;
        return $this;
    }

    public function isEmpty() {
        return empty($this->entity);
    }

    public function setEntity($entity) {
        $this->entity = $entity;
        return $this;
    }

    public function getEntity() {
        return $this->entity;
    }

    public function save() {
        entity_save($this->entity_type, $this->entity);
        return $this;
    }

    public function delete() {
        entity_delete($this->entity_type, $this->id());
        $this->entity = null;
    }

    public function fieldView($field, $view_mode = 'full', $langcode = NULL, $page = NULL) {
        
        $view = drupal_static(__CLASS__.'::'.__FUNCTION__.'::'.$field.'::'.$view_mode, array());
        if(count($view) == 0) {
            $view = entity_view($this->entity_type, array($this->entity), $view_mode, $langcode, $page);
            if(isset($view[$this->entity_type]) && is_array($view[$this->entity_type]) && count($view[$this->entity_type])) {
                $view = reset($view[$this->entity_type]);
            }
        }
        if(isset($view[$field])) {
            return $view[$field];
        }
        return false;
    }

    public function fieldViewRender($field, $view_mode = 'full', $langcode = NULL, $page = NULL) {
        $view = $this->fieldView($field, $view_mode, $langcode, $page);
        if($view) {
            return render($view);
        }
        return '';
    }
    
    public function setEntityType($type) {
        $this->entity_type = $type;
        $this->entity_info = entity_get_info($this->entity_type);
    }
    
    public function setBundle($value) {
        if(!empty($this->entity_info['entity keys']['bundle'])) {
            if(!$this->isEmpty())
                $this->{$this->entity_info['entity keys']['bundle']} = $value;
        }
        $this->bundle = $value;
        return $this;
    }
    
    public function bundle() {
        if(!empty($this->entity_info['entity keys']['bundle'])) {
            if(!$this->isEmpty())
                return $this->{$this->entity_info['entity keys']['bundle']};
        }
        return $this->bundle;
    }
    
    public function label() {
        if(!empty($this->entity_info['label'])) {
            return $this->entity_info['label'];
        }
        return '';
    }
    
    public function uri() {
        if(!$this->id()) return '';
        if(!empty($this->entity_info['uri callback'])) {
            $uri_callback = $this->entity_info['uri callback'];
            $arr = $uri_callback($this->entity);
            return $arr['path'];
        }
        return '';
    }

    public function alias() {
        $path = $this->uri();
        $result = $path;
        if(!empty($this->entity->language)) {
            $alias = drupal_lookup_path('alias', $path, $this->entity->language);
        } else {
            $alias = drupal_lookup_path('alias', $path);
        }
        if($alias) {
            $result = $alias;
        }
        return $result;
    }

    public function setAlias($alias) {
        path_delete(array('source' => $this->uri()));
        $path = array(
            'source' => $this->uri(),
            'alias' => $alias
        );
        path_save($path);
        return $this;
    }
    
    /**
     * edit form render
     * @see https://segmentfault.com/a/1190000006139669
     * @param array $form
     * @param array $form_state
     */
    public function edit(&$form, &$form_state)
    {
        field_attach_form($this->entity_type, $this->entity, $form, $form_state);
    }

    /**
     * @see https://segmentfault.com/a/1190000006139669
     * @param array $form
     * @param array $form_state
     */
    public function submit(&$form, &$form_state)
    {
        field_attach_submit($this->entity_type, $this->entity, $form, $form_state);
    }

    public function FieldManager() {
        return new FieldManager($this->entity_type, $this->entity);
    }
    
    public function getQuery() {
        $query = new \EntityFieldQuery();
        $query->entityCondition('entity_type', $this->entity_type)
            ->entityCondition('bundle', $this->bundle());
        return $query;
    }

    public function getFieldValueWithNotNull($field, $default_value='') {
        if($this->hasColumn($field, 'value')) return $this->get($field)->value;
        return $default_value;
    }

    public function printBacktrace() {
        $backtrace = array_slice(debug_backtrace(), 2, 5);
        foreach($backtrace as $key => $trace) {
            unset($backtrace[$key]['args']);
            unset($backtrace[$key]['object']);
        }
        watchdog('infinity', print_r($backtrace, true));
    }
}